package com.supreme.aspectserver.aspect;

import com.supreme.aspectserver.annotation.TestAnno;
import com.supreme.common.constant.ConsoleColors;
import com.supreme.commonboot.utils.ReturnEntity;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;

@Slf4j
@Aspect
@Component
public class TestAnnoAOP {

//    @Pointcut(value = "@within(com.supreme.aspectserver.annotation.TestAnno) || @annotation(com.supreme.aspectserver.annotation.TestAnno) || @args(com.supreme.aspectserver.annotation.TestAnno)")
    @Pointcut(value = "@within(com.supreme.aspectserver.annotation.TestAnno) || @annotation(com.supreme.aspectserver.annotation.TestAnno)")
    public void PointCut() {
    }

    @Before("PointCut()")
    public void beforeProcess(JoinPoint joinPoint) {
        ConsoleColors.println(ConsoleColors.RED_BOLD_BRIGHT, "beforeProcess");
        Class<?> clazz = joinPoint.getTarget().getClass();
        String clazzName = clazz.getName();
        log.info("<<Annotation>> Before clazzName={}", clazzName);
    }

    @After("PointCut()")
    public void afterProcess(JoinPoint joinPoint) {
        ConsoleColors.println(ConsoleColors.YELLOW_BOLD_BRIGHT, "afterProcess");
    }

    @AfterReturning(pointcut = "PointCut()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            System.out.println("arg = " + arg);
        }

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        TestAnno testAnno = method.getAnnotation(TestAnno.class);
        Optional.ofNullable(testAnno)
                .ifPresent(testAnno1 -> System.out.println("testAnno.value() = " + testAnno1.value()));

        ReturnEntity<?> returnEntity = (ReturnEntity<?>) result;
        ConsoleColors.println(ConsoleColors.BLUE_BOLD_BRIGHT, "afterReturning = " + returnEntity);
    }

    @AfterThrowing(pointcut = "PointCut()", throwing = "exception")
    public void afterThrowing(Throwable exception) {
        ConsoleColors.println(ConsoleColors.GREEN_BOLD_BRIGHT, "afterThrowing = " + exception.getMessage());
    }

    @Around("PointCut()")
    public Object aroundProcess(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        Class<?> declaringType = methodSignature.getDeclaringType();
        System.out.println("declaringType.getName() = " + declaringType.getName());
        TestAnno declaringTypeAnno = declaringType.getAnnotation(TestAnno.class);
        if (Objects.nonNull(declaringTypeAnno)) {
            System.out.println("declaringTypeAnno.value() = " + declaringTypeAnno.value());
        }

        Method method = methodSignature.getMethod();

        Class<?> declaringClass = method.getDeclaringClass();
        System.out.println("declaringClass.getName() = " + declaringClass.getName());
        TestAnno declaringClassAnno = declaringType.getAnnotation(TestAnno.class);
        if (Objects.nonNull(declaringClassAnno)) {
            System.out.println("declaringClassAnno.value() = " + declaringClassAnno.value());
        }

        TestAnno methodAnno = method.getAnnotation(TestAnno.class);
        if (Objects.nonNull(methodAnno)) {
            System.out.println("methodAnnotation.value() = " + methodAnno.value());
        }

        Class<?>[] parameterTypes = method.getParameterTypes();
        for (Class<?> parameterType : parameterTypes) {
            TestAnno argAnno = parameterType.getAnnotation(TestAnno.class);
            if (Objects.nonNull(argAnno)) {
                System.out.println("argAnno.value() = " + argAnno.value());
            }
        }

        Object proceed = joinPoint.proceed();
        log.info("[TestAnnoAspect] After");
        return proceed;
    }
}
